package com.lin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.lin.constant.UserConstants;
import com.lin.entity.R;
import com.lin.entity.dto.UserDTO;
import com.lin.entity.po.User;
import com.lin.entity.query.UserQuery;
import com.lin.entity.vo.PageVO;
import com.lin.entity.vo.UserVO;
import com.lin.exception.UserStatusException;
import com.lin.mapper.UserMapper;
import com.lin.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lin.utils.JWTUtil;
import com.lin.utils.RedisSerializerUtil;
import jakarta.annotation.Resource;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Resource
    private RedisSerializerUtil redisUtil;
    /**
     * 注册 --需要将密码加密处理
     * @param userDTO 用户信息
     * @return R
     */
    @Override
    public R register(UserDTO userDTO) {
        if (userDTO == null)
            return R.failed("用户信息不能为空", null);

        String username = userDTO.getUsername();
        String password = userDTO.getPassword();

        if (username == null || username.trim().isEmpty())
            return R.failed("用户名不能为空", null);

        if (password == null || password.trim().isEmpty())
            return R.failed("密码不能为空", null);

        // 加密密码 使用 org.mindrot.jbcrypt --hutool工具包中也有BCrypt
        String encryptedPassword = BCrypt.hashpw(password, BCrypt.gensalt());

        User user = new User(username, encryptedPassword, LocalDateTime.now());

        if (saveIfNotAbsent(user))
            return R.ok("注册成功", null);
        return R.failed("注册失败, 用户名已存在", null);
    }

    // 如果用户名不存在, 则保存用户信息并返回true
    private boolean saveIfNotAbsent(User user) {
        boolean flag = false;
        for (User user1 : list()) {
            if (user1.getUsername().equals(user.getUsername())) {
                flag = true;
                break;
            }
        }
        return !flag && save(user);
    }

    /**
     * 登录
     * @param userDTO 用户信息
     * @return R --将JWT令牌封装到返回结果中
     */
    @Override
    public R login(UserDTO userDTO) {
        if (userDTO == null)
            return R.failed("用户信息不能为空", null);

        String username = userDTO.getUsername();
        String password = userDTO.getPassword();

        if (isCorrect(username, password)) {
            // 匹配成功, 封装JWT令牌到返回结果
            String token = JWTUtil.getToken(Collections.singletonMap("username", username));
            return R.ok("登录成功", token);
        }
        return R.failed("登录失败, 用户名或密码错误", null);
    }

    // 判断用户名密码是否正确, 用户状态是否正常
    private boolean isCorrect(String username, String password) {
        for (User user : list()) {
            if (user.getUsername().equals(username)) {
                boolean check = BCrypt.checkpw(password, user.getPassword());
                if (!check) return false;
                if (user.getStatus() == 1)
                    return true;
                else if (user.getStatus() == 0)
                    throw new UserStatusException("状态异常, 账号已冻结");
                throw new UserStatusException("用户不存在");
            }
        }
        return false;
    }

    /**
     * 先查看Redis中是否存在当前用户信息, 有就直接返回,
     * 否则查询当前用户信息并将查询结果放到Redis中
     * @param username 用户名
     * @return UserVO
     */
    @Override
    public UserVO getCurrentUserInfo(String username) throws JsonProcessingException {
        // 用户名为空或空字符串, 返回null
        if (username == null || username.trim().isEmpty())
            return null;

        // 当前用户信息key
        String currentUserKey = UserConstants.CURRENT_USER_KEY_PREFIX + username;

        // 先查询Redis中是否有用户信息
        UserVO userVO = redisUtil.get(currentUserKey, UserVO.class);
        if (userVO != null)
            return userVO;

        // Redis中没有用户信息, 查询数据库并将查询结果添加到Redis中
        User user = lambdaQuery().eq(User::getUsername, username).one();

        // 查询结果为空, 返回null
        userVO = checkQueryStatus(user);
        redisUtil.set(currentUserKey, userVO); // 添加到Redis中
        return userVO;
    }

    /**
     * 检查查询结果状态是否为null或用户状态是否异常(0)
     * @param user 用户类
     * @return 校验通过返回UserVO实体, 失败抛出异常UserStatusException
     */
    private UserVO checkQueryStatus(User user) {
        if (user != null && user.getStatus() == 1)
            return BeanUtil.copyProperties(user, UserVO.class);
        else if (user != null && user.getStatus() == 0)
            throw new UserStatusException("状态异常, 账号已冻结");
        throw new UserStatusException("用户不存在");
    }

    /* 原获取当前用户信息, 直接查询数据库
    public UserVO getCurrentUserInfo(String username) {
        // 用户名为空或空字符串, 返回null
        if (username == null || username.trim().isEmpty())
            return null;
        User user = lambdaQuery().eq(User::getUsername, username).one();
        // 查询结果为空, 返回null
        if (user != null && user.getStatus() == 1)
            return BeanUtil.copyProperties(user, UserVO.class);

        else if (user != null && user.getStatus() == 0)
            throw new UserStatusException("状态异常, 账号已冻结");
        throw new UserStatusException("用户不存在");
    }*/

    /**
     * 分页查询用户信息
     * @param userQuery 查询条件
     * @return List<UserVO>
     */
    @Override
    public PageVO<UserVO> listUsers(UserQuery userQuery) throws JsonProcessingException {
        Integer pageNum = userQuery.getPage();
        Integer pageSize = userQuery.getPageSize();

        // 先查看Redis中是否有用户分页信息
        String userPageKey = UserConstants.USER_PAGE_KEY_PREFIX + pageNum + "_" + pageSize;

        PageVO<UserVO> userVOPageVO = redisUtil.get(userPageKey, new TypeReference<>() {});

        // 如果Redis中有分页信息, 直接返回
        if (userVOPageVO != null) {
            return userVOPageVO;
        }

        // 分页参数
        Page<User> page = Page.of(pageNum, pageSize);

        // 根据创建时间降序排序
        if (userQuery.getIsAsc())
            page.addOrder(OrderItem.asc("create_time"));
        else
            page.addOrder(OrderItem.desc("create_time"));

        // 用户状态条件
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("status", UserConstants.QUERY_STATUS);

        // Redis中没有分页信息, 数据库分页查询
        Page<User> p = page(page, userQueryWrapper);

        // 预声明返回结果
        PageVO<UserVO> pageVO = new PageVO<>();

        // 封装结果
        List<User> records = p.getRecords();

        // 设置分页信息
        pageVO.setPageNum((int) page.getCurrent());
        pageVO.setPageSize((int) page.getSize());
        pageVO.setTotal((int) page.getTotal());

        if (CollUtil.isNotEmpty(records)) {
            pageVO.setList(BeanUtil.copyToList(records, UserVO.class));
            // 将查询结果添加到Redis中, 默认十分钟失效
            redisUtil.set(userPageKey, pageVO);
        }

        return pageVO;
    }

    /**
     * 根据id查询用户信息, 若Redis中有对应信息, 直接返回,
     * 否则查询数据库, 并将查询结果添加到Redis中
     * @param id 用户Id
     * @return UserVO
     */
    @Override
    public UserVO getUserById(Integer id) throws JsonProcessingException {
        // 当前用户信息key
        String userInfoKey = UserConstants.USER_INFO_KEY_PREFIX + id;

        // 先查询Redis中是否有用户信息
        User user = redisUtil.get(userInfoKey, User.class);

        if (user != null)
            return BeanUtil.copyProperties(user, UserVO.class);

        // Redis中没有用户信息, 查询数据库并将查询结果添加到Redis中
        UserVO userVO = checkQueryStatus(getById(id));
        redisUtil.set(userInfoKey, userVO); // 默认十分钟失效
        return userVO;
    }

    /**
     * 删除用户--逻辑删除
     * @param id 用户ID
     * @return T/F
     */
    @Override
    public boolean logicRemoveById(Integer id) {
        User user = getById(id);

        if (user != null) {
            Integer status = user.getStatus();
            if (status == 0)
                throw new UserStatusException("状态异常, 账号已冻结");
            else if (status == 1)
                return lambdaUpdate()
                        .eq(User::getId, id)
                        .set(User::getStatus, UserConstants.LOGIC_DELETE_STATUS)
                        .update();
        }
        throw new UserStatusException("用户不存在");
    }
}
